import struct
from Charger_C3_protocol import *
from loguru import logger


class Charger_C3:
    def __init__(self, networkServer):
        self.networkServer = networkServer
        self.init_data()
        self.init_callback()

    def init_data(self):
        self.client_set = {}

    def init_callback(self):
        self.callback_map = {
            E_CmdCode.Login: self.handler_recv_login,
            E_CmdCode.Unlogin: self.handler_recv_unlogin,
            E_CmdCode.Unlogin_V2: self.handler_recv_unlogin,
            E_CmdCode.HeartBeat: self.handler_recv_HeartBeat,
            E_CmdCode.HeartBeat_V2: self.handler_recv_HeartBeat,
            E_CmdCode.FaultReport: self.handler_recv_FaultReport,
            E_CmdCode.StateReport: self.handler_recv_StateReport,
            E_CmdCode.ControlCharge: self.handler_recv_ControlCharge,
            E_CmdCode.ChargingConnectOK: self.handler_recv_ConnectOK,
            E_CmdCode.ChargingConnectOK_V2: self.handler_recv_ConnectOK,
        }

    def del_client(self, socket):
        """
        删除客户端, 通过socket判断
        """
        for key in self.Client_Set:
            if self.Client_Set[key].socket == socket:
                self.Client_Set.pop(key)

    async def handler_recv_msg(self, recv, socket):
        frame = Frame().from_bytes(recv)
        if frame == None:
            logger.debug(f"报文解析失败")
            return

        # 根据报文类型，调用回调函数
        cmd_code = frame.cmd_code
        if cmd_code in self.callback_map:
            logger.debug(f"收到报文cmd_code: {frame.cmd_code:02X}")
            await self.callback_map[cmd_code](frame, socket)
        else:
            logger.debug(f"未知 命令字cmd_code: {cmd_code:02X}")
            show_hex(recv, f"cmd_code: {cmd_code:02X}")

    async def handler_recv_login(self, frame, socket):
        """
        充电桩登录请求报文
        """
        client = ChargerClient()
        client.device_type = frame.device_type
        client.client_id = frame.client_id
        client.password, client.hw_version, client.sw_version, client.product_data = (
            struct.unpack("<IBBI", frame.data)
        )
        client.socket = socket
        self.client_set[client.client_id] = client
        logger.debug(f"请求登录 cliend_id: {client.client_id:02X}")
        await self.send_login_reply(frame, socket)

    async def send_login_reply(self, frame, socket):
        """
        登录响应报文
        """
        reply = Frame()
        reply.device_type = frame.device_type
        reply.client_id = frame.client_id
        reply.cmd_code = E_CmdCode.Login
        reply.data = struct.pack("<HBB", E_HeadId.Background_Service, 0, 0)
        await self.networkServer.send_data(socket, reply.to_bytes())

    async def handler_recv_unlogin(self, frame, socket):
        """
        充电桩注销请求报文
        """
        if frame.client_id in self.client_set:
            logger.debug(f"请求退出 cliend_id: {frame.client_id:02X}")
            del self.client_set[frame.client_id]

    async def handler_recv_HeartBeat(self, frame, socket):
        """
        心跳报文
        """

        def save_client_by_HeartBeat(self, frame, socket):
            client = ChargerClient()
            client.device_type = frame.device_type
            client.client_id = frame.client_id
            client.socket = socket
            self.client_set[client.client_id] = client

        if not frame.client_id in self.client_set:
            logger.info(f"未找到登录设备 client_id: {frame.client_id:02X}")
            save_client_by_HeartBeat(self, frame, socket)
            return

        chargerClnt = self.client_set[frame.client_id]
        (
            chargerClnt.charge_state,
            chargerClnt.cur_current,
            chargerClnt.temp,
            chargerClnt.battery_curve,
            chargerClnt.max_current,
        ) = struct.unpack_from("<BHBBH", frame.data)

        chargerClnt.real_current = round(chargerClnt.cur_current / 10, 1)
        logger.debug(
            f"心跳报文heartbeat: state:{chargerClnt.charge_state:02x} cur:{chargerClnt.real_current} curve:{chargerClnt.battery_curve} temp:{chargerClnt.temp}"
        )

        # 响应报文
        reply = Frame()
        reply.device_type = frame.device_type
        reply.client_id = frame.client_id
        reply.cmd_code = E_CmdCode.HeartBeat
        await self.networkServer.send_data(socket, reply.to_bytes())

    async def handler_recv_FaultReport(self, frame, socket):
        """
        故障上报
        bit0: 保留(兼容老版本)
        bit1: 电压异常
        bit2: 电流异常
        bit3: 充电桩掉线
        bit4: 预留
        bit5: 充电器温度异常报警(兼容老版本)
        bit6: 充电桩对接传感器损坏(一直处于触发状态)
        bit7: 烟雾传感器触发
        """
        fault_code = struct.unpack("<B", frame.data)[0]
        logger.error(f"故障上报 fault_code: {fault_code:02X}")

    async def handler_recv_StateReport(self, frame, socket):
        """
        状态上报:
        电流,电压,温度,电池曲线,最大电流,工作状态
        """
        if not frame.client_id in self.client_set:
            logger.info(f"StateReport 未找到设备client_id:{frame.client_id:02X}")
            return

        # 获取充电桩 客户端信息结构体
        clnt = self.client_set[frame.client_id]
        (
            clnt.charge_state,
            clnt.cur_current,
            clnt.cur_voltage,
            clnt.motor_state,
            clnt.motor_common_state,
            clnt.temp,
            clnt.battery_curve,
            clnt.max_current,
        ) = struct.unpack_from("<BHHBBBBH", frame.data)

        clnt.real_current = round(clnt.cur_current / 10, 1)
        clnt.real_voltage = round(clnt.cur_voltage / 10, 1)
        logger.debug(
            f"状态上报: state:{clnt.charge_state:02X} cur:{clnt.real_current} vol:{clnt.real_voltage} temp:{clnt.temp} motor:{clnt.motor_state:02X}"
        )

    async def handler_recv_ControlCharge(self, frame, socket):
        """
        控制充电桩:
            维护
            重启
            修改id
        """
        pass

    async def handler_recv_ActCmd(self, frame, socket):
        """
        充电桩激活指令:
            对接充电桩
            开始充电
            停止充电
            放弃对接
        """
        pass

        # ----------------------------------------
        # 主动发送控制命令
        # ----------------------------------------

    async def handler_recv_ConnectOK(self, frame, socket):
        """
        充电桩对接成功
        """
        logger.info(f"充电桩{frame.client_id:02X} connect对接成功")

    def hal_control_charging(self, chargingArgs: ChargingArgs):
        """
        控制充电桩
        """
        charger_clnt_id = chargingArgs.station_id
        if not charger_clnt_id in self.client_set:
            logger.info(f"充电桩未找到 client_id:{charger_clnt_id:02X}")
            return

        data = chargingArgs.to_bytes()
        charger_clnt = self.client_set[charger_clnt_id]
        send_frame = Frame(
            device_type=charger_clnt.device_type,
            client_id=charger_clnt.client_id,
            cmd_code=E_CmdCode.ChargingCmd,
            data=data,
        )
        send_data = send_frame.to_bytes()
        logger.warning(f"{chargingArgs}")
        show_hex(send_data, f"充电桩控制命令:{chargingArgs.cmd}")
        self.networkServer.async_task_send_data(charger_clnt.socket, send_data)
